﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

using MBT.Utils;

namespace MBT.Modeling
{
    /// <summary>
    /// Event in a test model
    /// </summary>
    public class TestEvent //: EnGine.Formalism.IFormalismElement
    {
        /// <summary>
        /// Constructor: extract information from "MethodInfo"s
        /// </summary>
        /// <param name="ID"></param>
        /// <param name="pre"></param>
        /// <param name="exe"></param>
        /// <param name="executableObj"></param>
        public TestEvent(string ID, MethodInfo pre, MethodInfo exe, ModelSlicer slicer, bool isControllable, object executableObj)
        {
            this.preInfo = pre;
            this.exeInfo = exe;
            id = ID;
            this.slicer = slicer;
            this._isControllable = isControllable;
            this.executableObject = executableObj;
            //* extract domains
            domains = new Dictionary<int, string>();
            for (int i = 0; i < exe.GetParameters().Length; i++)
            {
                ParameterInfo p = exe.GetParameters()[i];
                object[] attr = p.GetCustomAttributes(typeof(Domain), true);
                foreach (object at in attr)
                {
                    if (at is Domain)
                    {
                        domains.Add(i, (at as Domain).DomainID);
                        break;
                    }
                }
            }
        }

        Dictionary<int, string> domains;
        //public Function precondition2 = null;
        string id;
        bool _isControllable;

        public bool isControllable
        {
            get { return _isControllable; }
        }

        public bool evaluatePreconditionForEachParameter = true;



        #region Execution Directly
        MethodInfo preInfo;
        MethodInfo exeInfo;

        /// <summary>
        /// it is a list where each element is a list of possible values for one parameter
        /// </summary>
        List<object> paramValuesList;

        object executableObject;
        ModelSlicer slicer;

        public int XGenerateDomains()
        {
            paramValuesList = new List<object>();
            int size = 1;
            for (int i = 0; i < NumberOfParams; i++)
            {
                string funcName = "";
                string domianID;
                System.Collections.IList collection;

                //((ExecFunction)action).paramDomains.TryGetValue(((ExecFunction)action).execParameters[i].ID, out domianID);
                domains.TryGetValue(i, out domianID);
                slicer.domainFunctions.TryGetValue(domianID, out funcName);

                if (funcName == "")//No domain is defined!
                {
                    //if (((ExecFunction)action).execParameters[i].ParamType.IsEnum)
                    if (exeInfo.GetParameters()[i].ParameterType.IsEnum)
                    {
                        collection = FiniteDomainEnumarator.EnumerateEnum(exeInfo.GetParameters()[i].ParameterType);
                        //FiniteDomainEnumarator.EnumerateEnum(((ExecFunction)action).execParameters[i].ParamType);
                        size *= collection.Count;
                        paramValuesList.Add(collection);
                    }
                    //else if (((ExecFunction)action).execParameters[i].ParamType == typeof(bool))
                    else if (exeInfo.GetParameters()[i].ParameterType == typeof(bool))
                    {
                        collection = FiniteDomainEnumarator.EnumerateBools();
                        size *= collection.Count;
                        paramValuesList.Add(collection);
                    }
                    else
                    {
                        throw new Exception("No demain is defined!");
                    }
                }
                else
                {
                    //If the domain is function
                    object dom = executableObject.GetType().
                        InvokeMember(funcName, BindingFlags.InvokeMethod, null, executableObject, null);
                    //It might be variable
                    //??
                    //Or properties
                    //??
                    //Or null (if it is not specified or it is specified but not defined!
                    //??
                    //The domain might be static: in this case we are not required to call it each time
                    //
                    collection = dom as System.Collections.IList;
                    size *= collection.Count;
                    paramValuesList.Add(collection);
                }
            }
            return size;
        }

        /// <summary>
        /// Run an action by providing a list of values as parameters of method
        /// </summary>
        /// <param name="parameters"></param>
        public void XRunActionHandler(List<object> parameters)
        {
            this.exeInfo.Invoke(executableObject, BindingFlags.InvokeMethod, null, parameters.ToArray(),
                System.Globalization.CultureInfo.CurrentCulture);
        }

        /// <summary>
        /// Run precondition
        /// </summary>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public bool XRunPreconditionHandler(List<object> parameters)
        {
            return (bool)this.preInfo.Invoke(executableObject, 
			                                 BindingFlags.InvokeMethod, null, parameters.ToArray(),
                System.Globalization.CultureInfo.CurrentCulture);
        }

        /// <summary>
        /// Run Action
        /// </summary>
        /// <param name="instanceID"></param>
        public ActionInfo XRunActionHandler(int instanceID)
        {
            List<object> parameters = new List<object>();
            for (int i = 0; i < paramValuesList.Count; i++)
            {
                System.Collections.IList collection = paramValuesList[i] as System.Collections.IList;
                parameters.Add(collection[instanceID % collection.Count]);
                instanceID /= collection.Count;
            }
            XRunActionHandler(parameters);

            return new ActionInfo(parameters);

        }

        /// <summary>
        /// Run precondition
        /// </summary>
        /// <param name="instanceID"></param>
        /// <returns></returns>
        public bool XRunPreconditionHandler(int instanceID)
        {			
            List<object> parameters = new List<object>();
            for (int i = 0; i < paramValuesList.Count; i++)
            {
                System.Collections.IList collection = paramValuesList[i] as System.Collections.IList;
                parameters.Add(collection[instanceID % collection.Count]);
                instanceID /= collection.Count;
            }
            return XRunPreconditionHandler(parameters);			
        }
        #endregion

        /// <summary>
        /// Number of parameter
        /// </summary>
        public int NumberOfParams
        {
            get
            {
                return exeInfo.GetParameters().Length;
            }
        }


        /// <summary>
        /// Identifier of the event
        /// </summary>
        public string ID
        {
            get
            {
                return id;
            }
            set
            {
                id = value;
            }
        }
    }
}
